package org.jeecg.modules.database.sharding.algorithm;


import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Range;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.shardingsphere.sharding.api.sharding.standard.PreciseShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.RangeShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.StandardShardingAlgorithm;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * 按月分片算法
 *
 * @author 国子软件
 */
@Slf4j
public class ApiLogStrategyShardingAlgorithm implements StandardShardingAlgorithm<Date> {
    private static final DateTimeFormatter yyyyMM = DateTimeFormatter.ofPattern("yyyyMM");

    private Properties props = new Properties();


    /**
     * 用于处理=和IN的分片
     *
     * @param collection           目标分片的集合(表名)
     * @param preciseShardingValue 逻辑表相关信息
     * @return
     */
    @Override
    public String doSharding(Collection<String> collection, PreciseShardingValue<Date> preciseShardingValue) {

        log.info("preciseShardingValue: {}", JSON.toJSONString(preciseShardingValue));
        log.info("collection: {}", preciseShardingValue.toString());
        Date str = preciseShardingValue.getValue();
        LocalDateTime localDateTime = DateUtil.toLocalDateTime(str);
        String tableSuffix = localDateTime.format(yyyyMM);
        log.info("tableSuffix: {}", tableSuffix);
        //log.info("time: {}", time);
        if (StringUtils.isEmpty(tableSuffix)) {
            return collection.stream().findFirst().get();
        }
        // LocalDateTime value = LocalDateTime.parse(time, formatter);
        // String tableSuffix = value.format(yyyyMM);
        String logicTableName = preciseShardingValue.getLogicTableName();
        String table = logicTableName.concat("_").concat(tableSuffix);
        log.info("Log.doSharding table name: {}", table);
        return table;
        //return collection.stream().filter(s -> s.equals(table)).findFirst().orElseThrow(() -> new RuntimeException("逻辑分表不存在"));

//        for (String name : collection) {
//            Integer value = preciseShardingValue.getValue();
//            //根据值进行取模，得到一个目标值
//            if (name.indexOf(value % 2+"") > -1) {
//                return name;
//            }
//        }
//        throw new UnsupportedOperationException();
    }

    /**
     * 用于处理BETWEEN AND分片，如果不配置RangeShardingAlgorithm，SQL中的BETWEEN AND将按照全库路由处理
     *
     * @param collection
     * @param rangeShardingValue
     * @return
     */
    @Override
    public Collection<String> doSharding(Collection<String> collection, RangeShardingValue<Date> rangeShardingValue) {
        //逻辑表名
        String logicTableName = rangeShardingValue.getLogicTableName();
        System.out.println("logicTableName:"+logicTableName);
        ZoneId zoneId = ZoneId.systemDefault();
        //范围参数
        Range<Date> valueRange = rangeShardingValue.getValueRange();
        System.out.println("valueRange:"+valueRange);
        LocalDateTime begin = LocalDateTime.ofInstant(valueRange.lowerEndpoint().toInstant(), zoneId);
        LocalDateTime end = LocalDateTime.ofInstant(valueRange.upperEndpoint().toInstant(), zoneId);
        log.info("localtime: {} - {}", begin, end);

        Set<String> queryRangeTables = extracted(logicTableName, begin, end);

        ArrayList<String> tables = new ArrayList<>(collection);
        tables.retainAll(queryRangeTables);
        log.info("tables is: {}", tables);
        //System.out.println(JSON.toJSONString(tables));

        return tables;
    }

    /**
     * 根据范围计算表明
     *
     * @param logicTableName 逻辑表名
     * @param lowerEndpoint 范围起点
     * @param upperEndpoint 范围终端
     * @return 物理表名集合
     */
    private Set<String> extracted(String logicTableName, LocalDateTime lowerEndpoint, LocalDateTime upperEndpoint) {
        Set<String> rangeTable = new HashSet<>();
        while (lowerEndpoint.isBefore(upperEndpoint)) {
            String str = getTableNameByDate(lowerEndpoint, logicTableName);
            rangeTable.add(str);
            lowerEndpoint = lowerEndpoint.plusMonths(1);
        }
        // 获取物理表名
        String tableName = getTableNameByDate(upperEndpoint, logicTableName);
        rangeTable.add(tableName);
        return rangeTable;
    }

    /**
     * 根据日期获取表名
     * @param dateTime 日期
     * @param logicTableName 逻辑表名
     * @return 物理表名
     */
    private String getTableNameByDate(LocalDateTime dateTime, String logicTableName) {
        String tableSuffix = dateTime.format(yyyyMM);
        return logicTableName.concat("_").concat(tableSuffix);
    }

    /**
     * 初始化对象的时候调用的方法
     */
    @Override
    public void init() {
    }

    /**
     * 对应分片算法（sharding-algorithms）的类型
     *
     * @return
     */
    @Override
    public String getType() {
        return "STANDARD_MOD";
    }

    @Override
    public Properties getProps() {
        return this.props;
    }

    /**
     * 获取分片相关属性
     *
     * @param properties
     */
    @Override
    public void setProps(Properties properties) {
        this.props = properties;
    }
}
